﻿#region Usings

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;

#endregion

namespace Rail.Net.Core.Model.Nonpersistent
{
    [Serializable]
    [DataContract]
    [KnownType(typeof(MixedSequenceModel))]
    [KnownType(typeof(DomesticSequenceModel))]
    [KnownType(typeof(InternationalSequenceModel))]
    [KnownType(typeof(GlobalSequenceModel))]
    [KnownType(typeof(ContengentSequenceModel))]
    public abstract class SequenceModel : IEquatable<SequenceModel>
    {
        #region Constructors

        protected SequenceModel(ConnectionModel connection)
        {
            Connection = connection;
            Sequence = new SortedSet<StationModel>();
        }

        #endregion

        #region Properties

        public ConnectionModel Connection { get; set; }

        [DataMember]
        public SortedSet<StationModel> Sequence { get; set; }

        [DataMember]
        public int Index { get; set; }

        public string SegmentType
        {
            get { return GetType().Name.Replace("SequenceModel", ""); }
        }

        public StationModel FromStation
        {
            get { return Sequence.First(); }
        }

        public StationModel ToStation
        {
            get { return Sequence.Last(); }
        }

        public bool IsDomesticOnly
        {
            get { return Sequence.All(s => s.IsDomestic); }
        }

        public bool IsInternationalOnly
        {
            get { return Sequence.All(s => !s.IsDomestic); }
        }

        public bool IsMixed
        {
            get { return Sequence.Any(s => s.IsDomestic) && Sequence.Any(s => s.IsInternational); }
        }

        #endregion

        #region Methods

        public abstract SequenceModel Clone();

        #endregion

        public bool Equals(SequenceModel other)
        {
            return GetHashCode() == other.GetHashCode();
        }
    }

    [Serializable]
    [DataContract]
    public class MixedSequenceModel: SequenceModel
    {
        #region Constructors

        public MixedSequenceModel(ConnectionModel connection)
            : base(connection)
        {
        }

        #endregion

        #region Properties

        #endregion

        #region Methods

        public override SequenceModel Clone()
        {
            return new MixedSequenceModel(Connection)
            {
                Index = Index,
                Sequence = new SortedSet<StationModel>(Sequence)
            };
        }

        #endregion
    }

    [Serializable]
    [DataContract]
    public class DomesticSequenceModel : SequenceModel
    {
        #region Constructors

        public DomesticSequenceModel(ConnectionModel connection) : base(connection)
        {
            Distances = new List<Distance>();
        }

        #endregion

        #region Fields 

        private DistanceModel _distanceModel;

        #endregion

        #region Properties
        
        public List<Distance> Distances { get; set; }

        [DataMember]
        public DistanceModel DistanceModel
        {
            get
            {
                if (_distanceModel == null)
                {
                    var domesticDistance = new DistanceModel();
                    if (Distances != null)
                    {
                        foreach (var distance in Distances)
                        {
                            domesticDistance.DistanceOebb += distance.Oebb;
                            domesticDistance.DistanceDb += distance.Db;
                            domesticDistance.DistanceSbb += distance.Sbb;
                            domesticDistance.DistanceP10 += distance.P10;
                            domesticDistance.DistanceP20 += distance.P20;
                            domesticDistance.DistanceP30 += distance.P30;
                            domesticDistance.DistanceP31 += distance.P31;
                            domesticDistance.DistanceP40 += distance.P40;
                            domesticDistance.DistanceP41 += distance.P41;
                            domesticDistance.DistanceP42 += distance.P42;
                            domesticDistance.DistanceP43 += distance.P43;
                            domesticDistance.DistanceP50 += distance.P50;
                            domesticDistance.DistanceP51 += distance.P51;
                            domesticDistance.DistanceP52 += distance.P52;
                            domesticDistance.DistanceP53 += distance.P53;
                            domesticDistance.DistanceP60 += distance.P60;
                            domesticDistance.DistanceP80 += distance.P80;
                            domesticDistance.DistanceMav += distance.Mav;
                            domesticDistance.DistanceP81 += distance.P81;
                        }
                    }
                    domesticDistance.DistanceOebb +=
                        Sequence.Where(s => s.BorderModel != null).Select(s => s.BorderModel.Correction).Sum();
                    _distanceModel = domesticDistance;
                }
                return _distanceModel;
            }
            set { _distanceModel = value; }
        }

        #endregion

        #region Methods

        public override SequenceModel Clone()
        {
            return new DomesticSequenceModel(Connection)
                       {
                           Index = Index,
                           Distances = new List<Distance>(Distances),
                           Sequence = new SortedSet<StationModel>(Sequence)
                       };
        }

        #endregion
    }

    [Serializable]
    [DataContract]
    public class InternationalSequenceModel : SequenceModel
    {
        #region Properties

        public InternationalSequenceModel(ConnectionModel connection) : base(connection)
        {
        }

        [DataMember]
        public Relation Relation { get; set; }

        #endregion

        #region Methods

        public override SequenceModel Clone()
        {
            return new InternationalSequenceModel(Connection)
                       {
                           Index = Index,
                           Relation = Relation,
                           Sequence = new SortedSet<StationModel>(Sequence)
                       };
        }

        #endregion
    }

    [Serializable]
    [DataContract]
    public class GlobalSequenceModel : SequenceModel
    {
        #region Constructors

        public GlobalSequenceModel(ConnectionModel connection)
            : base(connection)
        {
        }

        #endregion

        #region Properties

        [DataMember]
        public TrainModel GlobalTrain { get; set; }

        #endregion

        #region Methods

        public override SequenceModel Clone()
        {
            return new GlobalSequenceModel(Connection)
                       {
                           Sequence = new SortedSet<StationModel>(Sequence)
                       };
        }
         
        #endregion
    }

    [Serializable]
    [DataContract]
    public class ContengentSequenceModel : SequenceModel
    {
        #region Constructors

        public ContengentSequenceModel(ConnectionModel connection)
            : base(connection)
        {
        }

        #endregion

        #region Properties

        [DataMember]
        public TrainModel ContengentTrain { get; set; }

        #endregion

        #region Methods

        public override SequenceModel Clone()
        {
            return new ContengentSequenceModel(Connection)
            {
                Sequence = new SortedSet<StationModel>(Sequence)
            };
        }

        #endregion
    }
}